package com.lzh.runke.core.aspect;

import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;
import com.lzh.runke.core.annotation.date.DateFieldType;
import com.lzh.runke.core.annotation.date.MaxOfDate;
import com.lzh.runke.core.annotation.date.MinOfDate;
import com.lzh.runke.core.entity.BaseEntity;
import com.lzh.runke.core.entity.spec.BaseSpec;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 时间参数极值处理
 *
 * @author xie
 * @date 2022-07-18
 */
@Slf4j
@Aspect
@Component
public class LimOfDateProcessorAspect {

    final static Multimap<DateFieldType, Integer> DATE_FILED_LIMITATION = ImmutableListMultimap.<DateFieldType, Integer>builder()
            .put(DateFieldType.YEAR, Calendar.DAY_OF_YEAR)
            .put(DateFieldType.YEAR, Calendar.HOUR_OF_DAY)
            .put(DateFieldType.YEAR, Calendar.MINUTE)
            .put(DateFieldType.YEAR, Calendar.SECOND)

            .put(DateFieldType.QUARTER, Calendar.DAY_OF_MONTH)
            .put(DateFieldType.QUARTER, Calendar.HOUR_OF_DAY)
            .put(DateFieldType.QUARTER, Calendar.MINUTE)
            .put(DateFieldType.QUARTER, Calendar.SECOND)

            .put(DateFieldType.MONTH, Calendar.DAY_OF_MONTH)
            .put(DateFieldType.MONTH, Calendar.HOUR_OF_DAY)
            .put(DateFieldType.MONTH, Calendar.MINUTE)
            .put(DateFieldType.MONTH, Calendar.SECOND)

            .put(DateFieldType.WEEK, Calendar.DAY_OF_WEEK)
            .put(DateFieldType.WEEK, Calendar.HOUR_OF_DAY)
            .put(DateFieldType.WEEK, Calendar.MINUTE)
            .put(DateFieldType.WEEK, Calendar.SECOND)

            .put(DateFieldType.DAY, Calendar.HOUR_OF_DAY)
            .put(DateFieldType.DAY, Calendar.MINUTE)
            .put(DateFieldType.DAY, Calendar.SECOND)

            .put(DateFieldType.HOUR, Calendar.MINUTE)
            .put(DateFieldType.HOUR, Calendar.SECOND)

            .put(DateFieldType.MINUTE, Calendar.SECOND)

            .build();

    /**
     * controller 调用前处理时间参数
     *
     * @param joinPoint
     */
    @Before("execution(* com.lzh.runke..controller..*(..))")
    public void doBefore(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        if (ArrayUtils.isNotEmpty(args)) {
            Arrays.stream(args).forEach(arg -> {
                // 仅处理参数类型基类为 BaseSpec BaseEntity 处理 DateParam
                if (arg instanceof BaseSpec || arg instanceof BaseEntity) {
                    processLimOfDate(arg);
                }
            });
        }
    }

    /**
     * 处理时间极值
     *
     * @param instance
     */
    private void processLimOfDate(Object instance) {
        List<Field> fields = FieldUtils.getAllFieldsList(instance.getClass());
        if (CollectionUtils.isNotEmpty(fields)) {
            fields.forEach(field -> {
                try {
                    Object value = FieldUtils.readField(field, instance, true);
                    if (null != value) {
                        // 子类递归
                        if (value instanceof BaseSpec || value instanceof BaseEntity) {
                            processLimOfDate(value);
                        } else if (value instanceof Date) {
                            Date lim = null;

                            MaxOfDate maxOfDate = AnnotationUtils.findAnnotation(field, MaxOfDate.class);
                            MinOfDate minOfDate = AnnotationUtils.findAnnotation(field, MinOfDate.class);
                            if (maxOfDate != null && minOfDate != null) {
                                throw new RuntimeException("@MaxOfDate, @MinOfDate 不能同时用于同一个属性");
                            } else if (null != maxOfDate) {
                                DateFieldType fieldType = maxOfDate.field();
                                lim = limOf((Date) value, fieldType, true);
                            } else if (null != minOfDate) {
                                DateFieldType fieldType = minOfDate.field();
                                lim = limOf((Date) value, fieldType, false);
                            } else {
                                lim = (Date) value;
                            }

                            FieldUtils.writeField(field, instance, lim, true);
                            return;
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }


    /**
     * 日期极值
     *
     * @param date
     * @param fieldType
     * @param max       极大值/极小值: true = max, false = min
     * @return
     */
    private static Date limOf(Date date, DateFieldType fieldType, boolean max) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        int limOfMillisecond = max ? 999 : 0;
        calendar.set(Calendar.MILLISECOND, limOfMillisecond); // 所有类型均设置毫秒数极值

        if (DateFieldType.SECOND.equals(fieldType)) {
            return calendar.getTime();
        }

        Collection<Integer> integers = DATE_FILED_LIMITATION.get(fieldType);
        integers.stream().forEach((field) -> {
            int actualLim;
            //TODO 这里为什么要用不同的方法获得最大与最小值
            if (field == Calendar.DAY_OF_YEAR || field == Calendar.DAY_OF_MONTH) {
                actualLim = max ? calendar.getActualMaximum(field) : calendar.getActualMinimum(field);
            } else {
                actualLim = max ? calendar.getMaximum(field) : calendar.getMinimum(field);
            }
            calendar.set(field, actualLim);
        });

        if (DateFieldType.WEEK.equals(fieldType)) {
            calendar.add(Calendar.DAY_OF_WEEK, 1); // 星期一为周的第一天
        }

        if (DateFieldType.QUARTER.equals(fieldType)) { // 季度
            int month = calendar.get(Calendar.MONTH); // month 从 0 开始
            int lastMonthOfQuarter = (month + 3) / 3 * 3 - 1;
            int offset = lastMonthOfQuarter - month;
            offset = max ? offset : offset - 2;
            calendar.add(Calendar.MONTH, offset);
        }

        return calendar.getTime();
    }

//    private Date lim(Date date, DateFieldType fieldType, boolean max) {
//
//    }
}